using System;
using System.IO;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms;
using DevExpress.CodeRush.Core;
using DevExpress.CodeRush.PlugInCore;
using DevExpress.CodeRush.StructuralParser;
using System.Reflection;
using Reflector;
using Reflector.CodeModel;
using EnvDTE;

namespace TestMechanic.Net
{
    [Title("Sense and Control")]
    public partial class SenseAndControl : ToolWindowPlugIn
    {

#region InitializePlugIn

        public override void InitializePlugIn()
        {
            base.InitializePlugIn();

            try
            {
                HintDirectories.Add(Path.GetDirectoryName(this.GetType().Assembly.Location));
                this.TestCoverageEventManager.Activate();
                AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(this.AssemblyResolve);
                this.TestCoverageEventManager.TestMethodActivated += new TestMethodActivatedEventHandler(this.TestMethodActivatedEventHandled);
            }
            catch (Exception ex)
            {
                throw;
            }
        }

#endregion

#region FinalizePlugIn

        public override void FinalizePlugIn()
        {
            //
            // TODO: Add your finalization code here.
            //
            AppDomain.CurrentDomain.AssemblyResolve -= new ResolveEventHandler(this.AssemblyResolve);
            this.TestCoverageEventManager.TestMethodActivated -= new TestMethodActivatedEventHandler(this.TestMethodActivatedEventHandled);
            this.TestCoverageEventManager.Deactivate();
            base.FinalizePlugIn();
        }

#endregion
        
#region Fields

        private TestCoverageEventManager _TestCoverageEventManager;
        private CodeRushEvents _DXCoreEvents;
        private IAssemblyManager _AssemblyLoader;
        private IServiceProvider _ServiceProvider;

#endregion
        
#region Properties

        public TestCoverageEventManager TestCoverageEventManager
        {
            get
            {
                if (_TestCoverageEventManager == null)
                    _TestCoverageEventManager = new TestCoverageEventManager();
                return _TestCoverageEventManager;
            }
            set
            {
                _TestCoverageEventManager = value;
            }
        }

        private string BinDirectory
        {
            get
            {
                string OutputDirectory;
                OutputDirectory = (string)CodeRush.Solution.Active.DTE.ActiveDocument.ProjectItem.ContainingProject.ConfigurationManager.ActiveConfiguration.Properties.Item(13).Value;
                OutputDirectory = Path.Combine(ProjectDirectory, OutputDirectory);
                return OutputDirectory;
            }
        }

        private string ProjectDirectory
        {
            get
            {
                string ProjectFilePath;
                string ProjectDirectory;
                ProjectFilePath = CodeRush.Solution.Active.DTE.ActiveDocument.ProjectItem.ContainingProject.FileName;
                ProjectDirectory = Path.GetDirectoryName(ProjectFilePath);
                return ProjectDirectory;
            }
        }

        public IServiceProvider ServiceProvider
        {
            get
            {
                if (_ServiceProvider == null)
                    _ServiceProvider = new Reflector.Application(null);
                return _ServiceProvider;
            }
        }

        private IAssemblyManager AssemblyLoader
        {
            get
            {
                _AssemblyLoader = (IAssemblyManager)this.ServiceProvider.GetService(typeof(IAssemblyManager));
                return _AssemblyLoader;
            }
        }

        public CodeRushEvents DXCoreEvents
        {
            get
            {
                if (_DXCoreEvents == null)
                    _DXCoreEvents = new CodeRushEvents();
                return _DXCoreEvents;
            }
            set
            {
                _DXCoreEvents = value;
            }
        }

#endregion

#region Event Handlers

        public void TestMethodActivatedEventHandled(TestMethodActivatedEventArgs eventArgs)
        {
            this.ShowNavPoint();

            string AssemblyName;
            string AssemblyPath;

            try
            {
                AssemblyName = eventArgs.TestSubjectAssemblyName;
                AssemblyPath = Path.Combine(this.BinDirectory, AssemblyName);
                this.GetInfo(AssemblyPath, eventArgs.TestSubjectMemberName, eventArgs.TestSubjectClassName, eventArgs.TestSubjectNamespace);
            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine(ex.ToString());
            }
        }

        public void ShowNavPoint()
        {
            LocatorBeacon Animie = new LocatorBeacon();
            int LineNumber = CodeRush.Source.ActiveMember.StartLine;
            DevExpress.CodeRush.Core.TextView ActiveTextView = CodeRush.TextViews.Active;
            int Column = CodeRush.Documents.ActiveTextDocument.GetLineLength(LineNumber) + 2;
            Animie.Start(ActiveTextView, LineNumber, Column);
        }

        public void GetInfo(string assemblyPath, String memberName, String className, String nameSpace)
        {
            IAssemblyManager assemblyLoader = this.AssemblyLoader;
            IAssembly rAssembly = assemblyLoader.LoadFile(assemblyPath);

            this.listViewMethodCalls.Items.Clear();
            foreach (IModule module in rAssembly.Modules)
            {
                foreach (ITypeDeclaration typeDeclaration in module.Types)
                {
                    if (typeDeclaration.Name == className && typeDeclaration.Namespace == nameSpace)
                    {
                        foreach (IMethodDeclaration methodDeclaration in typeDeclaration.Methods)
                        {
                            if (methodDeclaration.Name == memberName)
                            {
                                IMethodBody methodBody = methodDeclaration.Body as IMethodBody;
                                if (methodBody != null)
                                {
                                    foreach (IInstruction instruction in methodBody.Instructions)
                                    {
                                        if (instruction.Code == 40 ||
                                          instruction.Code == 111 ||
                                          instruction.Code == 41 ||
                                          instruction.Code == 115)
                                        {

                                            ITypeReference OwningType = ((ITypeReference)((IMethodReference)instruction.Value).DeclaringType);
                                            String[] Columns = new String[3] { instruction.Value.ToString(), string.Format("{0}.{1}", OwningType.Namespace, OwningType.Name), "" };
                                            ListViewItem Item = new ListViewItem(Columns);
                                            this.listViewMethodCalls.Items.Add(Item);
                                        }
                                    }
                                }
                            }

                        }
                    }
                }
            }

            assemblyLoader.Unload(rAssembly);
        }

#endregion
        
#region AssemblyLoader Resolution

        private StringCollection HintDirectories = new StringCollection();

        private Assembly AssemblyResolve(object sender, ResolveEventArgs args)
        {
            char[] Separators = new char[1] { ',' };
            string AsmName = args.Name.Split(Separators)[0];
            foreach (string Directory in HintDirectories)
            {
                string AsmPath = Path.Combine(Directory, string.Format("{0}.dll", AsmName));
                if (System.IO.File.Exists(AsmPath))
                {
                    return Assembly.LoadFrom(AsmPath);
                }
                AsmPath = Path.Combine(Directory, string.Format("{0}.exe", AsmName));
                if (System.IO.File.Exists(AsmPath))
                {
                    return Assembly.LoadFrom(AsmPath);
                }
            }
            return null;
        }

#endregion

    }
}